#include "Loader.h"
#include "GameSystemManager.h"
#include "NxOgreHelpers.h"

OgreMaxSceneCallbacks::OgreMaxSceneCallbacks(NxOgre::Scene* s) : OgreMaxSceneCallback ()
{
	//Copy scene
	NxScene = s;
	NxWorld = NULL;
}

OgreMaxSceneCallbacks::OgreMaxSceneCallbacks(NxOgre::World *w,Ogre::Vector3 initPos) : OgreMaxSceneCallback ()
{
	//Set the variables
	NxWorld = w;
	//NxScene = NULL;
	NxScene = GameSystemManager::getSingleton()->getPhysics()->getScene();
	mScenePosition = initPos;
}

void OgreMaxSceneCallbacks::LoadedUserData (const OgreMax::OgreMaxScene *scene, const Ogre::String &userDataReference, const Ogre::String &userData)
{
	CreateWorld (userData);
}

void OgreMaxSceneCallbacks::LoadingSceneFile(const OgreMaxScene* scene, const Ogre::String& fileName, Ogre::String& resourceGroupName) 
{
	/*
	if (allParams != "") {
		Ogre::String objectType = getParamValue("sceneInitPos",allParams);
		mScenePosition = Ogre::StringConverter::parseVector3(objectType);
	}
	*/
}

void OgreMaxSceneCallbacks::CreateWorld(Ogre::String allParams)
{

	Ogre::LogManager::getSingleton().logMessage("===Ogre=Max======================");
	Ogre::LogManager::getSingleton().logMessage("||Loading physics into scene...||");

	/*
	*If there is params to use
	*and no scene, create one,
	*else if there is no params
	*and no scene, create default
	*/

	if ((allParams != "") && !NxScene)
	{
		//Variables to extract info
		Ogre::String rootFolder = "";
		Ogre::String meshName = "";
		Ogre::String name = "";
		Ogre::String params = "";
		Ogre::String::iterator it = allParams.begin ();

		//Get root folder
		for (; it != allParams.end (); ++it)
		{
			if (*it != '\n')
				rootFolder += *it;
			else
				break;
		}
		//Get pass the \n
		++it;
		//Delete junk
		rootFolder.erase (--rootFolder.end());
		rootFolder.erase (--rootFolder.end());

		//Get the mesh-names if there is a root folder
		if (rootFolder != "")
		{
			for (; it != allParams.end(); ++it)
			{
				if (*it == '\n') //Finished
				{
					if (meshName != "")
					{
						//Remove junk
						meshName.erase(--meshName.end());
						meshName.erase(--meshName.end());
						//Add to resources
						NxOgre::Resources::ResourceSystem::getSingleton()->addMeshAs("file://" + rootFolder + "/" + meshName, meshName);
					}
					//Skip junk
					++it;
					break;
				}

				else if (*it == ';') //New name
				{
					if (meshName != "")
					{
						//Add to the resources
						NxOgre::Resources::ResourceSystem::getSingleton()->addMeshAs("file://" + rootFolder + "/" + meshName, meshName);
						//Increase the iterator to skip the ';'
						++it;
						//Get rid of any other junk characters
						while ((*it == ';') || (*it == ' '))
							++it;
						//Reset the mesh name
						meshName = "";
					}
				}
				else
					meshName += *it;
			}
		}

		//Get the name
		for (; it != allParams.end (); ++it)
		{
			if (*it != '\n')
				name += *it;
			else
				break;
		}
		//Get pass the \n
		++it;
		//Delete junk
		name.erase (--name.end());
		name.erase (--name.end());
		if (name == "")
		{
			name = "default";
			Ogre::LogManager::getSingleton().logMessage("||Warning! No scene name.      ||");
		}

		//Get the rest of the params
		for (; it != allParams.end(); ++it)
		{
			if (*it == '\n')
				break;
			else
				params += *it;
		}

		//Create the scene with the params
		NxScene = NxWorld->createScene(name, params);
		Ogre::LogManager::getSingleton().logMessage("||Scene created through params.||");

	}
	else if ((allParams != "") && !NxScene)
	{//create default scene
		NxScene = NxWorld->createScene ("default", "gravity: yes, floor: yes");
		Ogre::LogManager::getSingleton().logMessage("||WARNING!                     ||");
		Ogre::LogManager::getSingleton().logMessage("||No scene and no params.      ||");
		Ogre::LogManager::getSingleton().logMessage("||Created default scene.       ||");
	}
	else
		Ogre::LogManager::getSingleton().logMessage("||Provided scene used.         ||");

}

void OgreMaxSceneCallbacks::CreatedEntity (const OgreMax::OgreMaxScene* scene, Ogre::Entity* entity)
{
	Ogre::LogManager::getSingleton().logMessage("||Creating body...             ||");
	//It will only add if there is existing data
	if (scene->GetObjectExtraData(entity)->userData != "")
	{
		//Contains the entire param
		Ogre::String entireParam = scene->GetObjectExtraData(entity)->userData; 
		//Get params
		Ogre::String firstParam = getFirstParam (entireParam);	//Ex. "Box01;Box01.mesh"
		NxOgre::Shape* shape = getShape (entireParam);			//Ex. "Sphere" or "Sphere.nxs"
		Ogre::String lastParam = getLastParam (entireParam);	//Ex. "mass: 10"
		
		Ogre::String objectType = getParamValue("objectType",entireParam);

		//Special values
		if (firstParam == "") //Use name
			firstParam = entity->getName() + ";" + entity->getName() + ".mesh";

		//Copy object and data to the NxScene, if
		//all params are OK
		if ((firstParam != "") && (lastParam != "") && shape)
		{
			Vector3 objectPosition =  mScenePosition + entity->getParentSceneNode()->getPosition();
			
			if(objectType == "Body") {
				NxOgre::Body* mBody = NxScene->createBody(firstParam, shape, objectPosition, lastParam);
				rotateActor (mBody,entireParam);
			}
			else if(objectType == "Actor") {
				NxOgre::Actor* mActor = NxScene->createActor(firstParam,shape,objectPosition,lastParam);
			}
			entity->getParentSceneNode()->detachObject(entity);
		}
	}
}

void OgreMaxSceneCallbacks::FinishedLoad (const OgreMax::OgreMaxScene* scene, bool success)
{
	if (success)
		Ogre::LogManager::getSingleton().logMessage("||Load successful!             ||");
	else
		Ogre::LogManager::getSingleton().logMessage("||Load failed!                 ||");
	Ogre::LogManager::getSingleton().logMessage("=================================");
}

Ogre::String OgreMaxSceneCallbacks::getFirstParam (Ogre::String param)
{
	//Return empty if it is empty
	if (param != "")
	{
		Ogre::String firstParam = "";
		Ogre::String::iterator it = param.begin ();

		//Get first param
		for (; it != param.end(); ++it)
		{
			if (*it != '\n')
				firstParam += *it;
			else
			{
				if (firstParam != "")
				{
					//Get rid of the junk-character
					firstParam.erase(--firstParam.end());
					firstParam.erase(--firstParam.end());
				}
				break;
			}
		}
		return firstParam;
	}
	else
		return param;
}

NxOgre::Shape* OgreMaxSceneCallbacks::getShape (Ogre::String param)
{
	//Create iterator and param string
	Ogre::String::iterator it = param.begin ();
	Ogre::String shapeParam = "";
	
	//Get rid of the first param
	while ((it != param.end ()) && (*it != '\n'))
		++it;

	++it; //Skip the \n
	
	//Get the param string
	for (; it != param.end (); ++it)
	{
		if ((*it != ';') && (*it != '\n') && (*it != '\r'))
			shapeParam += *it;
		else
			break;
	}

	Ogre::String customType = getParamValue("customType",param);

	//Evaluate shapeParam (Why can't switch take Strings!?)
	if (shapeParam == "Sphere")
	{
		return new NxOgre::Sphere (getSphereSize (param, it));
	}
	if (shapeParam == "Box")
	{
		Ogre::LogManager::getSingleton().logMessage("||Box created.                 ||");

 		return new NxOgre::Cube (getCubeSize (param, it));
	}

	if (shapeParam == "Capsule")
	{
		Ogre::LogManager::getSingleton().logMessage("||Capsule created.                 ||");

		NxVec3 capsuleSize = getCapsuleSize(param, it);
		//NxReal height = NxConvert<NxReal, Ogre::Real>(getCapsuleSize(param, it).y);

		//return new NxOgre::Capsule(NxConvert<NxVec, Ogre::Vector3>(getCapsuleSize(param, it)));
		return new NxOgre::Capsule(capsuleSize.x,capsuleSize.y);
	}

	else if (customType == "Convex")
	{
		/*
		*Note:
		*This mesh must be added
		*prior to this call, might
		*be done through the scene-
		*settings. (User data)
		*/
		return getConvex (param, it);
	}
	else if (customType == "Triangle")
	{
		/*
		*Note:
		*This mesh must be added
		*prior to this call, might
		*be done through the scene-
		*settings. (User data)
		*/
		return getTriangle (param, it);
	}


	//Default (mostly to get rid of the warning)
	Ogre::LogManager::getSingleton().logMessage("||WARNING! Default shape used. ||");
	return new NxOgre::Cube (10);
}

NxOgre::SimpleShape* OgreMaxSceneCallbacks::getSimpleShape (Ogre::String param)
{
	//Create iterator and param string
	Ogre::String::iterator it = param.begin ();
	Ogre::String shapeParam = "";

	//Get rid of the first param
	while ((it != param.end ()) && (*it != '\n'))
		++it;

	++it; //Skip the \n

	//Get the param string
	for (; it != param.end (); ++it)
	{
		if ((*it != ';') && (*it != '\n') && (*it != '\r'))
			shapeParam += *it;
		else
			break;
	}

	if (shapeParam == "Plane")
	{
		Ogre::LogManager::getSingleton().logMessage("||Plane created.                 ||");

		//SimplePlane sp = new SimplePlane()
		NxVec3 planeNormal = getPlaneNormal (param, it);
		NxReal planeDistance = getPlaneDistance(param, it);

		NxPlane nPlane = NxPlane(planeNormal,planeDistance);

		return new NxOgre::SimplePlane(nPlane);

	}
	return NULL;
}

Ogre::String OgreMaxSceneCallbacks::getLastParam (Ogre::String param)
{
	//Needed variables
	Ogre::String lastParam = "";
	Ogre::String::iterator it = param.begin ();

	//Skip two first lines
	while ((*it != '\n') && (it != param.end()))
		++it;
	++it; //Skip the '\n'
	while ((*it != '\n') && (it != param.end()))
		++it;
	++it; //Skip the '\n'

	//Get the third line
	for (; it != param.end(); ++it)
	{
		if (*it == '\n')
			break;
		lastParam += *it;
	}

	//Get rid of junk (with safty check)
	if (lastParam.empty())
		throw Ogre::Exception (100, "Param empty.", "PhysicsCallback::getLastParam (Ogre::String param)");

	//Return
	return lastParam;
}

Ogre::String OgreMaxSceneCallbacks::getParamValue (Ogre::String paramName,Ogre::String param)
{
	string::size_type loc = param.find(paramName);

	if( loc != string::npos ) {
		Ogre::String paramValue = param.substr(loc + paramName.length() + 1,paramName.length() - (loc + paramName.length()) );
		Ogre::String res = "";

		Ogre::String::iterator i, iend;
		iend = paramValue.end();
		for (i = paramValue.begin(); i != iend; ++i)
		{
			if (*i != '\n' && (*i != '\r'))
				res += (*i);
			else
				break;
		}

		return res;
	}

	//Get rid of junk (with safty check)
	if (param.empty())
		throw Ogre::Exception (100, "Param empty.", "PhysicsCallback::getLastParam (Ogre::String param)");

	//Return
	return param;
}
Ogre::String OgreMaxSceneCallbacks::getMeshName (Ogre::String param)
{
	string::size_type loc = param.find(";");

	if( loc != string::npos ) {
		Ogre::String paramValue = param.substr(loc + 1,param.length());
		Ogre::String res = "";

		Ogre::String::iterator i, iend;
		iend = paramValue.end();
		for (i = paramValue.begin(); i != iend; ++i)
		{
			if (*i == '\n' || (*i == '\r'))
				break;
			res += (*i);
		}

		return res;
	}

	//Get rid of junk (with safty check)
	if (param.empty())
		throw Ogre::Exception (100, "Param empty.", "PhysicsCallback::getLastParam (Ogre::String param)");

	//Return
	return param;
}

Ogre::Real OgreMaxSceneCallbacks::getSphereSize(Ogre::String &param, Ogre::String::iterator it)
{
	return getNumber (param, it);
}

Ogre::Vector3 OgreMaxSceneCallbacks::getCubeSize(Ogre::String &param, Ogre::String::iterator it)
{
	Ogre::Real x, y, z;

	//Get numbers
	x = getNumber (param, it);
	y = getNumber (param, it);
	z = getNumber (param, it);

	return Ogre::Vector3 (x, y, z);
}
NxVec3 OgreMaxSceneCallbacks::getPlaneNormal(Ogre::String &param, Ogre::String::iterator it)
{
	Ogre::Real x, y, z;

	//Get numbers
	x = getNumber (param, it);
	y = getNumber (param, it);
	z = getNumber (param, it);

	return NxVec3 (x, y, z);
}
NxReal OgreMaxSceneCallbacks::getPlaneDistance(Ogre::String &param, Ogre::String::iterator it)
{
	return NxReal(getNumber (param, it));
}

NxVec3 OgreMaxSceneCallbacks::getCapsuleSize(Ogre::String &param, Ogre::String::iterator it)
{
	Ogre::Real x, y, z;

	//Get numbers
	x = getNumber (param, it);
	y = getNumber (param, it);
	z = getNumber (param, it);

	return NxVec3 (x, y, z);
}

NxOgre::Convex* OgreMaxSceneCallbacks::getConvex(Ogre::String &param, Ogre::String::iterator it)
{
	Ogre::String meshName = getMeshName(param);
	Ogre::String outFile = meshName.substr(0,meshName.length()-5);

	generateNXS(param);

	//Ogre::String path = "../../media/models/Penalty/" + meshName + "nxs";

	return new NxOgre::Convex(NxOgre::Resources::ResourceSystem::getSingleton()->getMesh(outFile));
}
NxOgre::TriangleMesh* OgreMaxSceneCallbacks::getTriangle(Ogre::String &param, Ogre::String::iterator it)
{

	Ogre::String meshName = getMeshName(param);
	Ogre::String outFile = meshName.substr(0,meshName.length()-5);

	generateNXS(param);

	//Ogre::String path = "../../media/models/Penalty/" + meshName + "nxs";

	return new NxOgre::TriangleMesh(NxOgre::Resources::ResourceSystem::getSingleton()->getMesh(outFile));
}

bool OgreMaxSceneCallbacks::generateNXS(Ogre::String &param) {

	Ogre::String meshName = getMeshName(param);
	Ogre::String outFile = meshName.substr(0,meshName.length()-5);

	//Path where .nxs files and flour.exe is located
	Ogre::String path = "H:\\Devel\\#PROJECTS\\Ogre\\PenaltyGame\\Media\\Models\\Penalty\\";
	Ogre::String cmdLine = path + "Flour.exe ";

	Ogre::String customType = getParamValue("customType",param);
	if(customType != "Triangle" && customType != "Convex")
		customType = "Convex";

	cmdLine +=  "meshes type: " + customType + ", in: " + path + meshName + ", out: " + path + outFile + ".nxs " + "> " + path + "conversionLog.txt";

	try {
		//if()
		system(cmdLine.c_str());
	} catch(exception& e) {
		//return false;
	}
	//Sleep(3000);
	
	Ogre::String mpath = "file://../../media/models/Penalty/" + outFile + ".nxs";

	NxOgre::Resources::ResourceSystem::getSingleton()->addMeshAs(mpath,outFile); 

	return true;
}

void OgreMaxSceneCallbacks::rotateActor (NxOgre::Actor* actor, Ogre::String params)
{
	//Necessary variables
	Ogre::String::iterator it = params.begin ();
	Ogre::Quaternion q;

	//Get rid of three first lines
	for (int i = 0; (it != params.end()) && (i < 3); it++)
		if (*it == '\n')
			++i;

	//Get numbers
	q.w = getNumber (params, it); //Can you guess how happy I was
	q.x = getNumber (params, it); //when I found out that 3ds Max
	q.y = getNumber (params, it); //uses Quaternions for rotation?
	q.z = getNumber (params, it);
	//Create quaternion

	//Rotate
	actor->setGlobalOrientation (q);
	
}

Ogre::Real OgreMaxSceneCallbacks::getNumber(Ogre::String &param, Ogre::String::iterator& it)
{
	Ogre::Real size; //Final size
	Ogre::String size_s = "";

	//Find the number!
	for (char c = *it, quit = false; (it != param.end()) && !quit; ++it, c = *it)
	{
		switch (*it)
		{
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case '0':
		case '-': //Negativ numbers can be handy...
			quit = true;
		}

	}

	//Go back to the number
	--it;

	//Get entire number
	for (; it != param.end(); ++it)
	{
		if ((*it == '\n') || (*it == '\r') || (*it == ' ') || (*it == ','))
			break;
		else
			size_s += *it;
	}

	std::istringstream convert(size_s); //Used for conversion

	//If the convert is successful, return
	if (convert >> size)
		return size;
	else
	{
		Ogre::LogManager::getSingleton().logMessage("||WARNING! Number incorrect.   ||");
		Ogre::LogManager::getSingleton().logMessage("||Default number used. (20)    ||");
		return 20;
	}
}